package net.cyndeline.scalarlib.rldungeon.dgs.graph


/**
 * Extended by vertex-classes, representing rooms in the map graph.
 *
 * IMPORTANT NOTICE TO USERS: This class requires equals() and hashcode() to be overwritten. The reason for this
 * is to allow users to generate specific levels by seeding the random number generator. There are multiple instances
 * in the level building algorithm where every room on the map is iterated over, and the order of these rooms may
 * (or may not) affect the final level. The order of rooms in these iterators depend on their hash values.
 * If these methods were not overwritten, seeding the Random object could still give different results on each build
 * attempt. Because of this, equals() and hashcode() must only depend on the fields of the room, and two rooms with
 * the same fields must always generate the same hash and be considered equal.
 *
 * @constructor Constructs a new room with the minimum amount of fields needed by the level strategy.
 * @param id a unique id for this room that is shared only by copies derived when modifying the room. This id should
 *           only be used when comparing rooms, do not assume that a particular id will end up being present in
 *           a graph.
 * @param distanceToGoal The number of edges that must be traversed to reach the level goal from this room (does not
 *                       take corridor/room content such as locked doors into consideration). Can be set to any value
 *                       by default by the implementing class, as it will be modified by the level strategy once
 *                       the map topology is finished.
 * @param pointlessAreaData Should be set to None by default. If the room represented by this object is deemed to be
 *                    pointless by the level generator (i.e the goal of the level can be reached without passing this
 *                    room), then this option will be set to contain all data related to the area of pointless rooms
 *                    this room belongs to. Note that this data isn't removed by any algorithm dealing with pointless
 *                    areas, to allow productions to filter rooms based on whether they used to be pointless or not.
 *                    If you want to check if a room is currently pointless, isPointless() should be used instead.
 * @param pointlessStatus True if this room is pointless, otherwise false. Should be set to false by default.
 */
abstract class Room[RoomType <: Room[RoomType]](val id: Int,
                                                val distanceToGoal: Int,
                                                val pointlessAreaData: Option[PointlessAreaData[RoomType]],
                                                pointlessStatus: Boolean) {

  /**
   * Checks the pointless status of this room.
   * @return True if the pointlessstatus is set, otherwise false.
   */
  final def isPointless: Boolean = pointlessStatus

  /**
   * Produces a copy of this room with a specified distance to the goal by setting the distanceToGoal parameter in
   * this class.
   *
   * @param d Distance to the goal from this room.
   * @return A copy of this room with the specified goal distance.
   */
  def withDistanceToGoal(d: Int): RoomType

  /**
   * Produces a copy of this room with a set pointless area data object.
   * @param data Pointless area data.
   * @return A copy of this room with the specified pointless area data.
   */
  def withPointlessData(data: PointlessAreaData[RoomType]): RoomType

  /**
   * Produces a copy of this room with its pointless status set by setting the pointlessStatus parameter in
   * this class.
   *
   * @param pointless True if this room is deemed pointless, otherwise false.
   * @return A copy of this room with its pointless status set.
   */
  def withPointlessStatus(pointless: Boolean): RoomType

  /**
   * Checks if the room can hold additional rewards. Used when dealing with pointless areas.
   * @return True if the room can hold additional rewards, otherwise false.
   */
  def canHoldAdditionalRewards: Boolean

  /**
   * Checks if the room can hold additional activators. Used when dealing with pointless areas.
   * @return True if the room can hold additional activators, otherwise false.
   */
  def canHoldAdditionalActivators: Boolean

  /**
   * Checks if this room is the start of a level.
   * @return True if the room is the starting point of the level, otherwise false.
   */
  def isStart: Boolean

  /**
   * Checks if this room is the goal of a level.
   * @return True if the room is the goal point of the level, otherwise false.
   */
  def isGoal: Boolean

  /**
   * @param other Room to compare this room to.
   * @return True if both rooms contain the same data (even if they're different object instances), otherwise false.
   */
  override def equals(other: Any): Boolean

  /**
   * @return An integer hash code based on the same data as equals.
   */
  override def hashCode: Int

}
